﻿using CarparkIMS_CMS.Models;
using CarparkIMS_CMS.SignalR;
using CarparkIMS_Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using CarparkIMS_CMS.Extensions;
using Microsoft.AspNetCore.Hosting;
using CarparkBusiness;
using Microsoft.Extensions.Options;
using Serilog;
using Common;
using System.Diagnostics;

namespace CarparkIMS_CMS.Controllers
{
    public class PlateRecognizeController : Controller
    {
        private readonly IMSContext _context;
        private readonly ILogger<PlateRecognizeController> _logger;
        readonly IHubContext<NotiHub> _hub;
        private IWebHostEnvironment _env;
        private ParkingSettingX ParkingSetting;
        public PlateRecognizeController(IMSContext iMSContext, ILogger<PlateRecognizeController> _logger, IHubContext<NotiHub> hub, IWebHostEnvironment _environment, IOptions<ParkingSettingX> parkingSetting)
        {
            _context = iMSContext;
            this._logger = _logger;
            this._hub = hub;
            _env = _environment;
            ParkingSetting = parkingSetting.Value;
        }
        public bool DeleteImage(int Id)
        {
            try
            {
                var plateLog = _context.llqqPlateLog.Select(c => new { c.ID, c.createdOn }).Where(c => c.ID == Id).FirstOrDefault();
                string date = string.Format("{0:yyyyMMdd}", plateLog.createdOn);
                string pfile = Path.Combine(_env.ContentRootPath, "img", date, $"{Id}.jpg");

                if (System.IO.File.Exists(pfile))
                {
                    System.IO.File.Delete(pfile);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {
                return false;
            }
        }
        [HttpGet]
        [Route("[controller]/[action]/{id}")]
        public FileResult GetImage(int Id)
        {
            try
            {
                var plateLog = _context.llqqPlateLog.Select(c => new { c.ID, c.createdOn }).Where(c => c.ID == Id).FirstOrDefault();
                string date = string.Format("{0:yyyyMMdd}", plateLog.createdOn);
                string pfile = Path.Combine(_env.ContentRootPath, "img", date, $"{Id}.jpg");

                if (!System.IO.File.Exists(pfile))
                {
                    pfile = Path.Combine(_env.ContentRootPath, "SampleNumberPlate.jpg");
                }
                byte[] image = System.IO.File.ReadAllBytes(pfile);
                return File(image, "image/jpeg", Id + ".jpg");
            }
            catch
            {
                string pfile = Path.Combine(_env.ContentRootPath, "SampleNumberPlate.jpg");
                byte[] image = System.IO.File.ReadAllBytes(pfile);
                return File(image, "image/jpeg", Id + ".jpg");
            }
        }
        public bool SaveImage(Image image, string filename)
        {
            try
            {
                using (var ms = new MemoryStream())
                {
                    image.Save(ms, ImageFormat.Jpeg);
                    ms.Position = 0;


                    string appPath = Path.Combine(_env.ContentRootPath, "img");

                    if (!Directory.Exists(appPath))
                    {
                        Directory.CreateDirectory(appPath);
                    }
                    string date = Util.GetDateFolderName();
                    string datePath = Path.Combine(_env.ContentRootPath, "img", date);
                    if (!Directory.Exists(datePath))
                    {
                        Directory.CreateDirectory(datePath);
                    }

                    var pathFilename = Path.Combine(appPath, filename);
                    if (System.IO.File.Exists(pathFilename))
                    {
                        System.IO.File.Delete(pathFilename);
                    }

                    using (System.IO.FileStream output = new System.IO.FileStream(pathFilename, FileMode.Create))
                    {
                        ms.CopyTo(output);
                        ms.Flush();
                        ms.Close();
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        [HttpPost]
        [Route("PlateReturn")]
        public async Task<IActionResult> PlateReturn([FromBody] VAXTORJson model)
        {
            var watch = Stopwatch.StartNew();

            if (model != null)
            {
                string filterForDateFromCamera = $"{DateTime.Now:yyyy-MM-dd}"; //臨時過濾掉屏幕上的日期
                if (model.plate.Contains(filterForDateFromCamera))
                {
                    return Json(new { SUCCESS = false, message = $"Here Is Date String,NOT A PLATE!!! (SOURCE PLATE VAL = {model.plate})" });
                }
                //FOR LOG 記錄 
                if (!string.IsNullOrEmpty(model.image64))
                {
                    VAXTORJson forLoger = new VAXTORJson
                    {
                        ip = model.ip,
                        plate = model.plate,
                        date = model.date,
                        image64 = model.image64.Substring(0, 20),
                        confidence = model.confidence,
                        country = model.country,
                        ParkingNoCalcType = model.ParkingNoCalcType,
                    };
                    forLoger.image64 = forLoger.image64.Substring(0, 20);
                    string jss64 = JsonConvert.SerializeObject(forLoger);
                    _logger.LogWarning($"LogWarning::{DateTime.Now:yyyy:MM:dd HH:mm:ss fff}{jss64}");
                    Common.CommonBase.OperateLoger($"{DateTime.Now:yyyy:MM:dd HH:mm:ss fff}{jss64}");
                }
                else
                {
                    string jss64Lesss = JsonConvert.SerializeObject(model);
                    _logger.LogWarning($"LogWarning::{DateTime.Now:yyyy:MM:dd HH:mm:ss fff}{jss64Lesss}");
                    Common.CommonBase.OperateLoger($"{DateTime.Now:yyyy:MM:dd HH:mm:ss fff}{jss64Lesss}");
                }
            }

            if (model != null)
            {
                DateTime OccuredDatetime = DatetimeHelper.ParseOccuredDatetime(model.date);

                string log1 = $"[FUNC:plateReturn {DateTime.Now:yyyy-MM-dd HH:mm:ss fff}] [plate={model.plate}, ip={model.ip}, date={model.date}, confidence={model.confidence}, country={model.country}, OccuredDatetime={OccuredDatetime:yyyy-MM-dd HH:mm:ss fff}, ParkingNoCalcType={model.ParkingNoCalcType}]";
                _logger.LogWarning(log1);

                bool canConnect = _context.Database.CanConnect();
                if (canConnect)
                {
                    _logger.LogWarning($"DATABASE::{DateTime.Now:yyyy:MM:dd HH:mm:ss fff} CAN CONNECT.....");
                }

                llqqPlateLog log = new llqqPlateLog();
                var parkingPlace = _context.llqqCameraDoorMapping.Where(a => a.CameraIP == model.ip).FirstOrDefault();
                if (parkingPlace == null)
                {
                    return Json(new { SUCCESS = false, message = $"no correponding parking place and camera ip registed! IP = {model.ip}" });
                }

                string parkingPlaceId = parkingPlace?.ParkingPlaceId ?? "0";

                log.ParkingPlaceId = parkingPlaceId;
                log.ip = model.ip;
                log.plate = model.plate;
                if (int.TryParse(model.confidence, out int confidance))
                    log.confidence = confidance;
                else
                    log.confidence = 0;

                log.createdOn = DateTime.Now;
                log.OccuredDatetime = OccuredDatetime;
                log.date = model.date;
                log.image64 = ""; // Util.compressBase64Image(model.image64);
                log.isValid = true;
                log.ParkingNoCalcType = model.ParkingNoCalcType;

                if (CheckNumberPlate(log.plate) || 1 == 1)  //跳过设置判断
                {
                    llqqPlateLog log2 = log;
                    log2.image64 = String.Empty;
                    _context.Add<llqqPlateLog>(log2);
                    bool saveDatabaseRes = _context.SaveChanges() > 0;

                    string logSaveSucc = $"[FUNC:plateReturn saveDatabaseRes = {saveDatabaseRes} [plate={model.plate}, ip={model.ip}, date={model.date}, confidence={model.confidence}, country={model.country}, OccuredDatetime={OccuredDatetime:yyyy-MM-dd HH:mm:ss fff}, ParkingNoCalcType={model.ParkingNoCalcType}]";
                    _logger.LogWarning(logSaveSucc);

                    if (!string.IsNullOrEmpty(model.image64))
                    {
                        //另外情况出来上存的Base64是为预留允许不上存Base64图片数据，允许空白。
                        string image64 = Util.compressBase64Image(model.image64);
                        Base64Save(image64, $"{log.ID}.jpg");
                    }

                    try
                    {
                        var door = _context.llqqCameraDoorMapping.Where(a => !a.Deleted && a.CameraIP == model.ip).FirstOrDefault();
                        if (door != null)
                        {
                            await _hub.Clients.All.SendAsync("newRecord", new llqqPlateLogViewModel(log, door.TransactionTypeID));
                        }
                    }
                    catch (Exception ex)
                    {
                        string logDoorCatch = $"[FUNC:plateReturn Handle Door Exception = {ex.Message} [plate={model.plate}, ip={model.ip}, date={model.date}, confidence={model.confidence}, country={model.country}, OccuredDatetime={OccuredDatetime:yyyy-MM-dd HH:mm:ss fff}, ParkingNoCalcType={model.ParkingNoCalcType}]";
                        _logger.LogWarning(logDoorCatch);
                        throw ex;
                    }

                    if (saveDatabaseRes)
                    {
                        watch.Stop();
                        return Ok(new { SUCCESS = true, id = log2.ID, message = $"Success!!!{log.OccuredDatetime:yyyy-MM-dd HH:mm:ss fff} | Time Elapsed={watch.Elapsed.TotalMilliseconds}ms" });
                    }
                    else
                    {
                        watch.Stop();
                        _logger.LogInformation("LOG SAVE FAIL!!!");
                        return Ok(new { SUCCESS = false, message = $"LOG SAVE FAIL!!! Time Elapsed={watch.Elapsed.TotalMilliseconds}ms" });
                    }
                }
                else
                {
                    watch.Stop();
                    _logger.LogInformation("LOG SAVE FAIL!!!");
                    return Ok(new { SUCCESS = false, message = $"LOG SAVE FAIL!!!Time Elapsed={watch.Elapsed.TotalMilliseconds}ms" });
                }
            }
            else
            {
                watch.Stop();
                string logstr = $"ParkingSetting?.OnlyRegistered={ParkingSetting?.OnlyRegistered} Time Elapsed={watch.Elapsed.TotalMilliseconds}ms";
                _logger.LogInformation(logstr);
                return Ok(new { SUCCESS = false, message = logstr });
            }
        }

        /// <summary>
        /// 上存图片到img目录
        /// 补充PlateReturn接口不上存图片，另独立上存处理（稳定一点）
        /// </summary>
        /// <param name="id"></param>
        /// <param name="file"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("PlateReturnUpFile/{id}")]
        public async Task<IActionResult> PlateReturnUpFile(long id, IFormFile file)
        {
            _logger.LogWarning("SAVE UPFILE : CAR PLATE JPG");

            UserFile userFile = new UserFile(file);

            string appPath = Path.Combine(_env.ContentRootPath, "img");

            if (!Directory.Exists(appPath))
            {
                Directory.CreateDirectory(appPath);
            }

            string date = Util.GetDateFolderName();
            string datePath = Path.Combine(_env.ContentRootPath, "img", date);
            if (!Directory.Exists(datePath))
            {
                Directory.CreateDirectory(datePath);
            }

            string filename = $"{id}.jpg";
            var pathFilename = Path.Combine(appPath, filename);
            await userFile.SaveAs(pathFilename);
            return Json(new { SUCCESS = true, message = filename });
        }

        public void Base64Save(string base64String, string filename)
        {
            if (string.IsNullOrEmpty(base64String))
                return;

            string appPath = Path.Combine(_env.ContentRootPath, "img");
            if (!Directory.Exists(appPath))
            {
                Directory.CreateDirectory(appPath);
            }

            string date = Util.GetDateFolderName();
            string datePath = Path.Combine(_env.ContentRootPath, "img", date);
            if (!Directory.Exists(datePath))
            {
                Directory.CreateDirectory(datePath);
            }

            var pathFilename = Path.Combine(appPath, date, filename);

            if (System.IO.File.Exists(pathFilename))
            {
                System.IO.File.Delete(pathFilename);
            }

            byte[] imageBytes = Convert.FromBase64String(base64String);

            using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
            {
                try
                {
                    Image image = Image.FromStream(ms);
                    image.Save(pathFilename, ImageFormat.Jpeg);
                    image.Dispose();
                    ms.FlushAsync();
                    ms.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"\n[FUNC::Base64Save] [EXCEPTION] [{ex.Message}] pathFilename = {pathFilename}\n");
                    return;
                }
            }
            return;
        }

        public bool CheckNumberPlate(string numberPlate)
        {
            if (ParkingSetting == null)
                return true;

            if (ParkingSetting?.OnlyRegistered == false)
            {
                return true;
            }

            bool isExistNumberPlate = _context.llqqPlateMapping.Where(c => c.PlateNo.Contains(numberPlate)).Any();

            return isExistNumberPlate;
        }

    }
}
